package framework.crypto;

import lombok.SneakyThrows;
import org.apache.commons.codec.binary.Hex;

import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
 * AES265接口
 */
public interface AESCrypto {

    /**
     * 加密算法
     */
    default String getAlgorithm() {
        return "AES";
    }

    /**
     * 加密解密算法/加密模式/填充方式
     */
    default String getCipherAlgorithm() {
        return "AES/GCM/NoPadding";
    }

    /**
     * 密钥
     */
    String getSecretKey();

    /**
     * 加密
     */
    @SneakyThrows
    default byte[] encode(byte[] content) {
        Cipher cipher = this.getEncodeCipher();
        byte[] d = cipher.doFinal(content);
        byte[] iv = cipher.getIV();
        byte[] buf = new byte[d.length + iv.length];
        System.arraycopy(iv, 0, buf, 0, iv.length);
        System.arraycopy(d, 0, buf, iv.length, d.length);
        return buf;
    }

    /**
     * 解密
     */
    @SneakyThrows
    default byte[] decode(byte[] content) {
        Cipher cipher = this.getDecodeCipher(content);
        //
        byte[] iv = cipher.getIV();
        byte[] data = new byte[content.length - iv.length];
        System.arraycopy(content, iv.length, data, 0, data.length);
        //
        return cipher.doFinal(data);
    }

    /**
     * 加密
     */
    default String encodeHex(String content) {
        byte[] contentBytes = content.getBytes(StandardCharsets.UTF_8);
        byte[] bytes = this.encode(contentBytes);
        return Hex.encodeHexString(bytes);
    }

    /**
     * 加密
     */
    default String encodeHex(byte[] content) {
        byte[] bytes = this.encode(content);
        return Hex.encodeHexString(bytes);
    }

    /**
     * 加密
     */
    default String encodeBase64(byte[] content) {
        byte[] bytes = this.encode(content);
        return Base64.getEncoder().encodeToString(bytes);
    }

    /**
     * 加密
     */
    default String encodeBase64(String content) {
        if (content == null)
            throw new IllegalArgumentException("content is null");
        return encodeBase64(content.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 加密
     */
    default String encodeUrlBase64(byte[] content) {
        byte[] bytes = this.encode(content);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
    }

    /**
     * 加密
     */
    default String encodeUrlBase64(String content) {
        if (content == null)
            throw new IllegalArgumentException("content is null");
        return encodeUrlBase64(content.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 解密
     */
    default String decodeHex(String content) {
        return new String(this.decode(content), StandardCharsets.UTF_8);
    }

    /**
     * 解密
     */
    @SneakyThrows
    default byte[] decode(String hexContent) {
        byte[] contentBytes = Hex.decodeHex(hexContent);
        return this.decode(contentBytes);
    }

    /**
     * 解密
     */
    default byte[] decodeBase64(String base64Content) {
        byte[] contentBytes = Base64.getDecoder().decode(base64Content);
        return this.decode(contentBytes);
    }

    /**
     * 解密
     */
    default String decodeBase64AsString(String base64Content) {
        byte[] base64 = decodeBase64(base64Content);
        if (base64 == null) return null;
        return new String(base64, StandardCharsets.UTF_8);
    }

    /**
     * 解密
     */
    default byte[] decodeUrlBase64(String base64Content) {
        byte[] contentBytes = Base64.getUrlDecoder().decode(base64Content);
        return this.decode(contentBytes);
    }

    /**
     * 解密
     */
    default String decodeUrlBase64AsString(String base64Content) {
        byte[] base64 = this.decodeUrlBase64(base64Content);
        if (base64 == null) return null;
        return new String(base64, StandardCharsets.UTF_8);
    }

    /**
     * 获取密码处理实例
     *
     * @return
     */
    Cipher getEncodeCipher();

    /**
     * 获取密码处理实例
     *
     * @param encryptedData
     * @return
     */
    Cipher getDecodeCipher(byte[] encryptedData);

    /**
     * 是否启用
     *
     * @return
     */
    default boolean enable() {
        return true;
    }

}
